using System.Collections.Concurrent;
using EasyNetQ.Internals;

namespace Crux.Core.EasyNetQ;
/// <summary>
/// EasyNetQFacade
/// </summary>
public class EasyNetQFacade(
    IRabbitMqBusAccessor rabbitMqBusAccessor,
    ILogger<EasyNetQFacade> logger)
    : IEasyNetQFacade
{
    #region private fields
    private readonly IBus _bus = rabbitMqBusAccessor.TryGetBus() ?? throw new ArgumentNullException(nameof(_bus));
    private readonly ILogger<EasyNetQFacade> _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    private readonly ConcurrentQueue<SubscriptionResult> subscribers = new();
    /// <inheritdoc/>
    #endregion
    public bool IsDisposed { get; private set; }
    /// <inheritdoc/>
    public async Task PublishAsync<T>(
        T message,
        Action<IPublishConfiguration> configure,
        CancellationToken cancellationToken = default)
        => await _bus.PubSub.PublishAsync(message, configure, cancellationToken).ConfigureAwait(false);
    /// <inheritdoc/>
    public AwaitableDisposable<SubscriptionResult> SubscribeAsync<T>(
        string subscriptionId,
        Func<T, CancellationToken, Task> onMessage,
        Action<ISubscriptionConfiguration> configure,
        CancellationToken cancellationToken = default)
    {
        Func<Task<SubscriptionResult>> invoke = async () =>
        {
            var subscriptionResult = await _bus.PubSub.SubscribeAsync(subscriptionId, onMessage, configure, cancellationToken).ConfigureAwait(false);

            subscribers.Enqueue(subscriptionResult);

            return subscriptionResult;
        };

        return new AwaitableDisposable<SubscriptionResult>(invoke());
    }
    /// <inheritdoc/>
    public void Dispose()
    {
        if (IsDisposed) return;

        IsDisposed = true;

        while (subscribers.TryDequeue(out var result))
        {
            try
            {
                using var _ = result;
            }
            catch (Exception e)
            {
                _logger.LogError(e, e.Message);
            }
        }

        GC.SuppressFinalize(this);
    }
}